#ifndef COMMON_VECTOR_H
#define COMMON_VECTOR_H

#include <vector>
#include <iterator>
#include <algorithm>
#include "Common/Collections/IIterator.h"

#include "Common/Collections/IIterable.h"
#include "Common/Collections/ICollection.h"
#include "Common/Collections/IVector.h"

#include "Common/Collections/VectorIterator.h"
#include "Common/Collections/IteratorPtr.h"

namespace cmn {

	template<typename T>
	class Vector : public IVector<T>
	{
	private:
		std::vector<T> m_vector;

	public:
		Vector() {}

		Vector( const IIterable<T>* source )	{
			for( IteratorPtr<T> sourceIter( source ); !sourceIter.IsEnd(); sourceIter.Next() ) {
				m_vector.push_back( sourceIter.Current() );
			}
		}

		Vector( const IIterable<T>& source )	{
			for( IteratorPtr<T> sourceIter( source ); !sourceIter.IsEnd(); sourceIter.Next() ) {
				m_vector.push_back( sourceIter.Current() );
			}
		}

		virtual ~Vector() {}

		virtual IIterator<T>* GetIterator() const {
			return new VectorIterator<T> ( &m_vector );
		}

		virtual T& operator[]( int index ) {
			return m_vector[index];
		}

		virtual const T& operator[]( int index ) const {
			return m_vector[index];
		}

		virtual T GetAt( int index ) const {
			return m_vector[index];
		}

		virtual void SetAt( int index, T value ) {
			m_vector[index] = value;
		}

		virtual int GetLength() const {
			return m_vector.size();
		}

		virtual void Add( T item ) {
			m_vector.push_back( item );
		}

		virtual void AddRange( IIterable<T>* iterable ) {
			for( IteratorPtr<T> items( iterable ); !items.IsEnd(); items.Next() ) {
				m_vector.push_back( items.Current() );
			}
		}

		virtual void Insert( int index, T item ) {
			m_vector.insert( m_vector.begin() + index, item );
		}

		virtual void Remove( T item ) {
			m_vector.erase( std::remove( m_vector.begin(), m_vector.end(), item ), m_vector.end() );
		}

		virtual void Remove( bool ( *predicate )( const T& item ) ) {
			m_vector.erase( std::remove_if( m_vector.begin(), m_vector.end(), predicate ), m_vector.end() );
		}

		virtual void RemoveAt( int index ) {
			m_vector.erase( m_vector.begin() + index );
		}

		virtual void Clear() {
			m_vector.clear();
		}

		virtual bool Contains( T item ) const {
			return std::find( m_vector.begin(), m_vector.end(), item ) != m_vector.end();
		}

		virtual int IndexOf( T item ) const {
			return ( int ) std::distance( m_vector.begin(), std::find( m_vector.begin(), m_vector.end(), item ) );
		}

		virtual int IndexOf( bool ( *predicate )( const T& item ) ) const {
			return ( int ) std::distance( m_vector.begin(), std::find_if( m_vector.begin(), m_vector.end(), predicate ) );
		}

		virtual T Find( bool ( *predicate )( const T& item ) ) const {
			return *std::find_if( m_vector.begin(), m_vector.end(), predicate );
		}

		virtual void Sort( bool ( *predicate )( const T& itemA, const T& itemB ) ) {
			std::sort( m_vector.begin(), m_vector.end(), predicate );
		}

		virtual void SortStable( bool ( *predicate )( const T& itemA, const T& itemB ) ) {
			std::stable_sort( m_vector.begin(), m_vector.end(), predicate );
		}

		virtual std::vector<T> GetStdVector() const {
			std::vector<T> result;
			std::copy( m_vector.begin(), m_vector.end(), std::back_inserter( result ) );
			return result;
		}

	};
}

#endif
